﻿using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Security.AccessControl;
using Baik.Interface.Execution;
using Baik.Interface.Framework;

namespace Baik.Execution
{
    public class Boolean : BuiltInObject, IBoolean
    {
        private IExecutionEnvironment environ { get; set; }
        public Boolean(IInjector inject, IExecutionEnvironment environ) : base(inject)
        {
            this.environ = environ;
            Dictionary.Add("whenTrue",  BuiltInFactory.Function(inject, WhenTrue));
            Dictionary.Add("whenFalse",  BuiltInFactory.Function(inject, WhenTrue));
            Dictionary.Add("and",  BuiltInFactory.Function(inject, And));
            Dictionary.Add("or",  BuiltInFactory.Function(inject, Or));
            Dictionary.Add("xor",  BuiltInFactory.Function(inject, Xor));
            Dictionary.Add("not",  BuiltInFactory.Function(inject, Not));
            Dictionary.Add("nand",  BuiltInFactory.Function(inject, Nand));
            Dictionary.Add("nor",  BuiltInFactory.Function(inject, Nor));
        }

        private IObject WhenTrue(IEnumerable<IObject> prms)
        {
            if (!Value) return this;
            foreach (IFunction fd in prms.OfType<IFunction>())
            {
                (fd as IFunction).Call(new List<IObject>());
            }
            return this;
        }
        private IObject WhenFalse(IEnumerable<IObject> prms)
        {
            if (Value) return this;
            foreach (IFunction fd in prms.OfType<IFunction>())
            {
                (fd as IFunction).Call(new List<IObject>());
            }
            return this;
        }

        private IObject And(IEnumerable<IObject> prms)
        {
            var rtn = Value;
            if (!rtn) return Inject.Instantiate<IFalse>();
            if (prms.OfType<IBoolean>().Any(b => !b.Value)) return Inject.Instantiate<IFalse>();
            return Inject.Instantiate<ITrue>();
        }
        private IObject Or(IEnumerable<IObject> prms)
        {
            var rtn = Value;
            if (rtn || (prms.OfType<IBoolean>().Any(b => b.Value))) return Inject.Instantiate<ITrue>();
            return Inject.Instantiate<IFalse>();
        }
        private IObject Xor(IEnumerable<IObject> prms)
        {
            
            int count = Value ? 1 : 0;
            foreach (IObject prm in prms)
            {
                IBoolean b = prm as IBoolean;
                if (b != null)
                {
                    if (b.Value) count++;
                }
            }
            if (count == 1) return Inject.Instantiate<ITrue>();
            return Inject.Instantiate<IFalse>();
        }
        private IObject Nand(IEnumerable<IObject> prms)
        {
            var rtn = (IBoolean) this.And(prms);
            return (rtn.Value ? (IBoolean) Inject.Instantiate<ITrue>() : (IBoolean) Inject.Instantiate<IFalse>());
        }
        private IObject Nor(IEnumerable<IObject> prms)
        {
            var rtn = (IBoolean) this.Or(prms);
            return (rtn.Value ? (IBoolean) Inject.Instantiate<ITrue>() : (IBoolean) Inject.Instantiate<IFalse>());
        }
        private IObject Not(IEnumerable<IObject> prms)
        {
            return (Value ? (IBoolean) Inject.Instantiate<ITrue>() : (IBoolean) Inject.Instantiate<IFalse>());
        }
        public bool Value { get; set; }
    }
}